package com.ramnight.unify.boot.component;

import java.time.Duration;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class EncryptComponent {
	private static final String PAPER = "1=AST;2=DFY6;3=GH4;4=JK9;5=LZP8;6=XC7;7=VB3;8=NM0;9=QW1;0=EI2;X=UOR5";
	private static final int[] ORDER_PATH = new int[] { 19, 15, 20, 3, 0, 6, 12, 9, 2, 23, 14, 1, 8, 17, 4, 13, 18, 10, 5, 22 };
	private static final int[] CHECKER_PATH = new int[] { 16, 7, 21, 11 };
	private static final int CHECKER_SALT = 723;

	private Map<Integer, char[]> num2char = Maps.newHashMap();
	private Map<Character, Integer> char2num = Maps.newHashMap();
	//截止符，标识数字已填充完毕，需要填充mask了
	private Set<Character> cutter = Sets.newHashSet();
	//掩码池，用来填充
	private char[] mask;
	//字符选择器，一个数字会对应多个字符，每次需要选择第几个
	private int selector;

	@PostConstruct
	private void init() {
		for (String single : PAPER.split(";")) {
			if (StringUtils.isBlank(single)) {
				continue;
			}
			String[] p = single.split("=");
			if ("X".equals(p[0])) {
				char[] chars = p[1].toCharArray();
				for (char c : chars) {
					cutter.add(c);
				}
			} else {
				int num = Integer.parseInt(p[0]);
				char[] chars = p[1].toCharArray();

				num2char.put(num, chars);
				for (char c : chars) {
					char2num.put(c, num);
				}
			}
		}

		generateMask();
	}

	@Scheduled(cron = "0 10 0 * * ? ")
	// @Scheduled(fixedDelay = 1000)
	private void generateMask() {
		// 5位数，需要凑满24位，long最大19位，所以拆分位两部分去叠加
		long dayMills = System.currentTimeMillis() / Duration.ofDays(1).toMillis();
		// 9位
		long salt1 = RandomUtils.nextLong(1_000000000L, 9_000000000L);
		// 6位
		long salt2 = RandomUtils.nextLong(1_000000L, 9_000000L);
		String maskNumStr = String.valueOf(dayMills * salt1) + String.valueOf(dayMills * salt2);
		// mask倒数第三位做为selector
		selector = Character.getNumericValue(maskNumStr.charAt(maskNumStr.length() - 3));
		mask = numStringToCharString(maskNumStr).toCharArray();
		log.info("mask refreshed, mask = {}, selector = {}", mask, selector);
	}

	private String numStringToCharString(String numString) {
		StringBuilder sb = new StringBuilder();
		for (char nc : numString.toCharArray()) {
			sb.append(getCharByNum(Character.getNumericValue(nc)));
		}
		return sb.toString();
	}

	//将数字逐个放到空位，放完后用cutter标识已放完，后面全部填充mask
	public String encodeId(long id) {
		long left = id;
		char[] result = new char[24];
		boolean isCutterSet = false;
		int maskIdx = 0;
		for (int pos : ORDER_PATH) {
			if (left > 0) {
				int cur = (int) (left % 10);
				result[pos] = getCharByNum(cur);
				left = left / 10;
			} else {
				if (!isCutterSet) {
					result[pos] = getCutter();
					isCutterSet = true;
				} else {
					result[pos] = mask[maskIdx];
					maskIdx++;
				}
			}
		}

		setChecker(result);
		return new String(result);
	}

	private void setChecker(char[] chars) {
		int total = 0;
		for (int cur : ORDER_PATH) {
			total += chars[cur];
		}
		String str = String.valueOf((total * CHECKER_SALT)); // 取前4位

		chars[CHECKER_PATH[0]] = getCharByNum(Character.getNumericValue(str.charAt(0)));
		chars[CHECKER_PATH[1]] = getCharByNum(Character.getNumericValue(str.charAt(1)));
		chars[CHECKER_PATH[2]] = getCharByNum(Character.getNumericValue(str.charAt(2)));
		chars[CHECKER_PATH[3]] = getCharByNum(Character.getNumericValue(str.charAt(3)));
	}

	private boolean check(String code) {
		StringBuilder sb = new StringBuilder();
		sb.append(getNumByChar(code.charAt(CHECKER_PATH[0])));
		sb.append(getNumByChar(code.charAt(CHECKER_PATH[1])));
		sb.append(getNumByChar(code.charAt(CHECKER_PATH[2])));
		sb.append(getNumByChar(code.charAt(CHECKER_PATH[3])));

		String checker = sb.toString();

		int total = 0;
		for (int cur : ORDER_PATH) {
			total += code.charAt(cur);
		}
		return String.valueOf(total * CHECKER_SALT).substring(0, 4).equals(checker);
	}

	private char getCutter() {
		Character[] dict = cutter.toArray(new Character[cutter.size()]);
		int cIdx = selector % dict.length;
		return dict[cIdx];
	}

	private int getNumByChar(char c) {
		return char2num.get(c);
	}

	private char getCharByNum(int num) {
		char[] dict = num2char.get(num);
		int cIdx = selector % dict.length;
		return dict[cIdx];
	}

	public long decodeId(String str) {
		if (str == null || str.length() != 24 || !check(str)) {
			return -1;
		}
		StringBuilder sb = new StringBuilder();
		for (int pos : ORDER_PATH) {
			char c = str.charAt(pos);
			if (cutter.contains(c)) {
				break;
			} else {
				sb.insert(0, getNumByChar(c));
			}
		}
		return Long.parseLong(sb.toString());
	}
}
